// Inflate.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2010-January-08 18:32:12>
//
// ------------------------------------------------------------------
//
// This module defines classes for decompression. This code is derived
// from the jzlib implementation of zlib, but significantly modified.
// The object model is not the same, and many of the behaviors are
// different.  Nonetheless, in keeping with the license for jzlib, I am
// reproducing the copyright to that code here.
//
// ------------------------------------------------------------------
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
// and contributors of zlib.
//
// -----------------------------------------------------------------------


using System;
namespace Ionic.Zlib
{
  sealed class InflateBlocks
  {
    private const int MANY = 1440;

    // Table for deflate from PKZIP's appnote.txt.
    internal static readonly int[] border = new int[]
    { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };

    private enum InflateBlockMode
    {
      TYPE = 0,                     // get type bits (3, including end bit)
      LENS = 1,                     // get lengths for stored
      STORED = 2,                     // processing stored block
      TABLE = 3,                     // get table lengths
      BTREE = 4,                     // get bit lengths tree for a dynamic block
      DTREE = 5,                     // get length, distance trees for a dynamic block
      CODES = 6,                     // processing fixed or dynamic block
      DRY = 7,                     // output remaining window bytes
      DONE = 8,                     // finished last block, done
      BAD = 9,                     // ot a data error--stuck here
    }

    private InflateBlockMode mode;                    // current inflate_block mode

    internal int left;                                // if STORED, bytes left to copy

    internal int table;                               // table lengths (14 bits)
    internal int index;                               // index into blens (or border)
    internal int[] blens;                             // bit lengths of codes
    internal int[] bb = new int[1];                   // bit length tree depth
    internal int[] tb = new int[1];                   // bit length decoding tree

    internal InflateCodes codes = new InflateCodes(); // if CODES, current state

    internal int last;                                // true if this block is the last block

    internal ZlibCodec _codec;                        // pointer back to this zlib stream

    // mode independent information
    internal int bitk;                                // bits in bit buffer
    internal int bitb;                                // bit buffer
    internal int[] hufts;                             // single malloc for tree space
    internal byte[] window;                           // sliding window
    internal int end;                                 // one byte after sliding window
    internal int readAt;                              // window read pointer
    internal int writeAt;                             // window write pointer
    internal System.Object checkfn;                   // check function
    internal uint check;                              // check on output

    internal InfTree inftree = new InfTree();

    internal InflateBlocks(ZlibCodec codec, System.Object checkfn, int w)
    {
      _codec = codec;
      hufts = new int[MANY * 3];
      window = new byte[w];
      end = w;
      this.checkfn = checkfn;
      mode = InflateBlockMode.TYPE;
      Reset();
    }

    internal uint Reset()
    {
      uint oldCheck = check;
      mode = InflateBlockMode.TYPE;
      bitk = 0;
      bitb = 0;
      readAt = writeAt = 0;

      if (checkfn != null)
        _codec._Adler32 = check = Adler.Adler32(0, null, 0, 0);
      return oldCheck;
    }


    internal int Process(int r)
    {
      int t; // temporary storage
      int b; // bit buffer
      int k; // bits in bit buffer
      int p; // input data pointer
      int n; // bytes available there
      int q; // output window write pointer
      int m; // bytes to end of window or read pointer

      // copy input/output information to locals (UPDATE macro restores)

      p = _codec.NextIn;
      n = _codec.AvailableBytesIn;
      b = bitb;
      k = bitk;

      q = writeAt;
      m = (int)(q < readAt ? readAt - q - 1 : end - q);


      // process input based on current state
      while (true)
      {
        switch (mode)
        {
          case InflateBlockMode.TYPE:

            while (k < (3))
            {
              if (n != 0)
              {
                r = ZlibConstants.Z_OK;
              }
              else
              {
                bitb = b; bitk = k;
                _codec.AvailableBytesIn = n;
                _codec.TotalBytesIn += p - _codec.NextIn;
                _codec.NextIn = p;
                writeAt = q;
                return Flush(r);
              }

              n--;
              b |= (_codec.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }
            t = (int)(b & 7);
            last = t & 1;

            switch ((uint)t >> 1)
            {
              case 0:  // stored
                b >>= 3; k -= (3);
                t = k & 7; // go to byte boundary
                b >>= t; k -= t;
                mode = InflateBlockMode.LENS; // get length of stored block
                break;

              case 1:  // fixed
                int[] bl = new int[1];
                int[] bd = new int[1];
                int[][] tl = new int[1][];
                int[][] td = new int[1][];
                InfTree.inflate_trees_fixed(bl, bd, tl, td, _codec);
                codes.Init(bl[0], bd[0], tl[0], 0, td[0], 0);
                b >>= 3; k -= 3;
                mode = InflateBlockMode.CODES;
                break;

              case 2:  // dynamic
                b >>= 3; k -= 3;
                mode = InflateBlockMode.TABLE;
                break;

              case 3:  // illegal
                b >>= 3; k -= 3;
                mode = InflateBlockMode.BAD;
                _codec.Message = "invalid block type";
                r = ZlibConstants.Z_DATA_ERROR;
                bitb = b; bitk = k;
                _codec.AvailableBytesIn = n;
                _codec.TotalBytesIn += p - _codec.NextIn;
                _codec.NextIn = p;
                writeAt = q;
                return Flush(r);
            }
            break;

          case InflateBlockMode.LENS:

            while (k < (32))
            {
              if (n != 0)
              {
                r = ZlibConstants.Z_OK;
              }
              else
              {
                bitb = b; bitk = k;
                _codec.AvailableBytesIn = n;
                _codec.TotalBytesIn += p - _codec.NextIn;
                _codec.NextIn = p;
                writeAt = q;
                return Flush(r);
              }
                ;
              n--;
              b |= (_codec.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
            {
              mode = InflateBlockMode.BAD;
              _codec.Message = "invalid stored block lengths";
              r = ZlibConstants.Z_DATA_ERROR;

              bitb = b; bitk = k;
              _codec.AvailableBytesIn = n;
              _codec.TotalBytesIn += p - _codec.NextIn;
              _codec.NextIn = p;
              writeAt = q;
              return Flush(r);
            }
            left = (b & 0xffff);
            b = k = 0; // dump bits
            mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE);
            break;

          case InflateBlockMode.STORED:
            if (n == 0)
            {
              bitb = b; bitk = k;
              _codec.AvailableBytesIn = n;
              _codec.TotalBytesIn += p - _codec.NextIn;
              _codec.NextIn = p;
              writeAt = q;
              return Flush(r);
            }

            if (m == 0)
            {
              if (q == end && readAt != 0)
              {
                q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q);
              }
              if (m == 0)
              {
                writeAt = q;
                r = Flush(r);
                q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q);
                if (q == end && readAt != 0)
                {
                  q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q);
                }
                if (m == 0)
                {
                  bitb = b; bitk = k;
                  _codec.AvailableBytesIn = n;
                  _codec.TotalBytesIn += p - _codec.NextIn;
                  _codec.NextIn = p;
                  writeAt = q;
                  return Flush(r);
                }
              }
            }
            r = ZlibConstants.Z_OK;

            t = left;
            if (t > n)
              t = n;
            if (t > m)
              t = m;
            Array.Copy(_codec.InputBuffer, p, window, q, t);
            p += t; n -= t;
            q += t; m -= t;
            if ((left -= t) != 0)
              break;
            mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE;
            break;

          case InflateBlockMode.TABLE:

            while (k < (14))
            {
              if (n != 0)
              {
                r = ZlibConstants.Z_OK;
              }
              else
              {
                bitb = b; bitk = k;
                _codec.AvailableBytesIn = n;
                _codec.TotalBytesIn += p - _codec.NextIn;
                _codec.NextIn = p;
                writeAt = q;
                return Flush(r);
              }

              n--;
              b |= (_codec.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            table = t = (b & 0x3fff);
            if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
            {
              mode = InflateBlockMode.BAD;
              _codec.Message = "too many length or distance symbols";
              r = ZlibConstants.Z_DATA_ERROR;

              bitb = b; bitk = k;
              _codec.AvailableBytesIn = n;
              _codec.TotalBytesIn += p - _codec.NextIn;
              _codec.NextIn = p;
              writeAt = q;
              return Flush(r);
            }
            t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
            if (blens == null || blens.Length < t)
            {
              blens = new int[t];
            }
            else
            {
              Array.Clear(blens, 0, t);
              // for (int i = 0; i < t; i++)
              // {
              //     blens[i] = 0;
              // }
            }

            b >>= 14;
            k -= 14;


            index = 0;
            mode = InflateBlockMode.BTREE;
            goto case InflateBlockMode.BTREE;

          case InflateBlockMode.BTREE:
            while (index < 4 + (table >> 10))
            {
              while (k < (3))
              {
                if (n != 0)
                {
                  r = ZlibConstants.Z_OK;
                }
                else
                {
                  bitb = b; bitk = k;
                  _codec.AvailableBytesIn = n;
                  _codec.TotalBytesIn += p - _codec.NextIn;
                  _codec.NextIn = p;
                  writeAt = q;
                  return Flush(r);
                }

                n--;
                b |= (_codec.InputBuffer[p++] & 0xff) << k;
                k += 8;
              }

              blens[border[index++]] = b & 7;

              b >>= 3; k -= 3;
            }

            while (index < 19)
            {
              blens[border[index++]] = 0;
            }

            bb[0] = 7;
            t = inftree.inflate_trees_bits(blens, bb, tb, hufts, _codec);
            if (t != ZlibConstants.Z_OK)
            {
              r = t;
              if (r == ZlibConstants.Z_DATA_ERROR)
              {
                blens = null;
                mode = InflateBlockMode.BAD;
              }

              bitb = b; bitk = k;
              _codec.AvailableBytesIn = n;
              _codec.TotalBytesIn += p - _codec.NextIn;
              _codec.NextIn = p;
              writeAt = q;
              return Flush(r);
            }

            index = 0;
            mode = InflateBlockMode.DTREE;
            goto case InflateBlockMode.DTREE;

          case InflateBlockMode.DTREE:
            while (true)
            {
              t = table;
              if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)))
              {
                break;
              }

              int i, j, c;

              t = bb[0];

              while (k < t)
              {
                if (n != 0)
                {
                  r = ZlibConstants.Z_OK;
                }
                else
                {
                  bitb = b; bitk = k;
                  _codec.AvailableBytesIn = n;
                  _codec.TotalBytesIn += p - _codec.NextIn;
                  _codec.NextIn = p;
                  writeAt = q;
                  return Flush(r);
                }

                n--;
                b |= (_codec.InputBuffer[p++] & 0xff) << k;
                k += 8;
              }

              t = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 1];
              c = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 2];

              if (c < 16)
              {
                b >>= t; k -= t;
                blens[index++] = c;
              }
              else
              {
                // c == 16..18
                i = c == 18 ? 7 : c - 14;
                j = c == 18 ? 11 : 3;

                while (k < (t + i))
                {
                  if (n != 0)
                  {
                    r = ZlibConstants.Z_OK;
                  }
                  else
                  {
                    bitb = b; bitk = k;
                    _codec.AvailableBytesIn = n;
                    _codec.TotalBytesIn += p - _codec.NextIn;
                    _codec.NextIn = p;
                    writeAt = q;
                    return Flush(r);
                  }

                  n--;
                  b |= (_codec.InputBuffer[p++] & 0xff) << k;
                  k += 8;
                }

                b >>= t; k -= t;

                j += (b & InternalInflateConstants.InflateMask[i]);

                b >>= i; k -= i;

                i = index;
                t = table;
                if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1))
                {
                  blens = null;
                  mode = InflateBlockMode.BAD;
                  _codec.Message = "invalid bit length repeat";
                  r = ZlibConstants.Z_DATA_ERROR;

                  bitb = b; bitk = k;
                  _codec.AvailableBytesIn = n;
                  _codec.TotalBytesIn += p - _codec.NextIn;
                  _codec.NextIn = p;
                  writeAt = q;
                  return Flush(r);
                }

                c = (c == 16) ? blens[i - 1] : 0;
                do
                {
                  blens[i++] = c;
                }
                while (--j != 0);
                index = i;
              }
            }

            tb[0] = -1;
            {
              int[] bl = new int[] { 9 };  // must be <= 9 for lookahead assumptions
              int[] bd = new int[] { 6 }; // must be <= 9 for lookahead assumptions
              int[] tl = new int[1];
              int[] td = new int[1];

              t = table;
              t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl, bd, tl, td, hufts, _codec);

              if (t != ZlibConstants.Z_OK)
              {
                if (t == ZlibConstants.Z_DATA_ERROR)
                {
                  blens = null;
                  mode = InflateBlockMode.BAD;
                }
                r = t;

                bitb = b; bitk = k;
                _codec.AvailableBytesIn = n;
                _codec.TotalBytesIn += p - _codec.NextIn;
                _codec.NextIn = p;
                writeAt = q;
                return Flush(r);
              }
              codes.Init(bl[0], bd[0], hufts, tl[0], hufts, td[0]);
            }
            mode = InflateBlockMode.CODES;
            goto case InflateBlockMode.CODES;

          case InflateBlockMode.CODES:
            bitb = b; bitk = k;
            _codec.AvailableBytesIn = n;
            _codec.TotalBytesIn += p - _codec.NextIn;
            _codec.NextIn = p;
            writeAt = q;

            r = codes.Process(this, r);
            if (r != ZlibConstants.Z_STREAM_END)
            {
              return Flush(r);
            }

            r = ZlibConstants.Z_OK;
            p = _codec.NextIn;
            n = _codec.AvailableBytesIn;
            b = bitb;
            k = bitk;
            q = writeAt;
            m = (int)(q < readAt ? readAt - q - 1 : end - q);

            if (last == 0)
            {
              mode = InflateBlockMode.TYPE;
              break;
            }
            mode = InflateBlockMode.DRY;
            goto case InflateBlockMode.DRY;

          case InflateBlockMode.DRY:
            writeAt = q;
            r = Flush(r);
            q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q);
            if (readAt != writeAt)
            {
              bitb = b; bitk = k;
              _codec.AvailableBytesIn = n;
              _codec.TotalBytesIn += p - _codec.NextIn;
              _codec.NextIn = p;
              writeAt = q;
              return Flush(r);
            }
            mode = InflateBlockMode.DONE;
            goto case InflateBlockMode.DONE;

          case InflateBlockMode.DONE:
            r = ZlibConstants.Z_STREAM_END;
            bitb = b;
            bitk = k;
            _codec.AvailableBytesIn = n;
            _codec.TotalBytesIn += p - _codec.NextIn;
            _codec.NextIn = p;
            writeAt = q;
            return Flush(r);

          case InflateBlockMode.BAD:
            r = ZlibConstants.Z_DATA_ERROR;

            bitb = b; bitk = k;
            _codec.AvailableBytesIn = n;
            _codec.TotalBytesIn += p - _codec.NextIn;
            _codec.NextIn = p;
            writeAt = q;
            return Flush(r);


          default:
            r = ZlibConstants.Z_STREAM_ERROR;

            bitb = b; bitk = k;
            _codec.AvailableBytesIn = n;
            _codec.TotalBytesIn += p - _codec.NextIn;
            _codec.NextIn = p;
            writeAt = q;
            return Flush(r);
        }
      }
    }


    internal void Free()
    {
      Reset();
      window = null;
      hufts = null;
    }

    internal void SetDictionary(byte[] d, int start, int n)
    {
      Array.Copy(d, start, window, 0, n);
      readAt = writeAt = n;
    }

    // Returns true if inflate is currently at the end of a block generated
    // by Z_SYNC_FLUSH or Z_FULL_FLUSH.
    internal int SyncPoint()
    {
      return mode == InflateBlockMode.LENS ? 1 : 0;
    }

    // copy as much as possible from the sliding window to the output area
    internal int Flush(int r)
    {
      int nBytes;

      for (int pass = 0; pass < 2; pass++)
      {
        if (pass == 0)
        {
          // compute number of bytes to copy as far as end of window
          nBytes = (int)((readAt <= writeAt ? writeAt : end) - readAt);
        }
        else
        {
          // compute bytes to copy
          nBytes = writeAt - readAt;
        }

        // workitem 8870
        if (nBytes == 0)
        {
          if (r == ZlibConstants.Z_BUF_ERROR)
            r = ZlibConstants.Z_OK;
          return r;
        }

        if (nBytes > _codec.AvailableBytesOut)
          nBytes = _codec.AvailableBytesOut;

        if (nBytes != 0 && r == ZlibConstants.Z_BUF_ERROR)
          r = ZlibConstants.Z_OK;

        // update counters
        _codec.AvailableBytesOut -= nBytes;
        _codec.TotalBytesOut += nBytes;

        // update check information
        if (checkfn != null)
          _codec._Adler32 = check = Adler.Adler32(check, window, readAt, nBytes);

        // copy as far as end of window
        Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes);
        _codec.NextOut += nBytes;
        readAt += nBytes;

        // see if more to copy at beginning of window
        if (readAt == end && pass == 0)
        {
          // wrap pointers
          readAt = 0;
          if (writeAt == end)
            writeAt = 0;
        }
        else pass++;
      }

      // done
      return r;
    }
  }


  internal static class InternalInflateConstants
  {
    // And'ing with mask[n] masks the lower n bits
    internal static readonly int[] InflateMask = new int[] {
            0x00000000, 0x00000001, 0x00000003, 0x00000007,
            0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
            0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
            0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff };
  }


  sealed class InflateCodes
  {
    // waiting for "i:"=input,
    //             "o:"=output,
    //             "x:"=nothing
    private const int START = 0; // x: set up for LEN
    private const int LEN = 1; // i: get length/literal/eob next
    private const int LENEXT = 2; // i: getting length extra (have base)
    private const int DIST = 3; // i: get distance next
    private const int DISTEXT = 4; // i: getting distance extra
    private const int COPY = 5; // o: copying bytes in window, waiting for space
    private const int LIT = 6; // o: got literal, waiting for output space
    private const int WASH = 7; // o: got eob, possibly still output waiting
    private const int END = 8; // x: got eob and all data flushed
    private const int BADCODE = 9; // x: got error

    internal int mode;        // current inflate_codes mode

    // mode dependent information
    internal int len;

    internal int[] tree;      // pointer into tree
    internal int tree_index = 0;
    internal int need;        // bits needed

    internal int lit;

    // if EXT or COPY, where and how much
    internal int bitsToGet;   // bits to get for extra
    internal int dist;        // distance back to copy from

    internal byte lbits;      // ltree bits decoded per branch
    internal byte dbits;      // dtree bits decoder per branch
    internal int[] ltree;     // literal/length/eob tree
    internal int ltree_index; // literal/length/eob tree
    internal int[] dtree;     // distance tree
    internal int dtree_index; // distance tree

    internal InflateCodes()
    {
    }

    internal void Init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index)
    {
      mode = START;
      lbits = (byte)bl;
      dbits = (byte)bd;
      ltree = tl;
      ltree_index = tl_index;
      dtree = td;
      dtree_index = td_index;
      tree = null;
    }

    internal int Process(InflateBlocks blocks, int r)
    {
      int j;      // temporary storage
      int tindex; // temporary pointer
      int e;      // extra bits or operation
      int b = 0;  // bit buffer
      int k = 0;  // bits in bit buffer
      int p = 0;  // input data pointer
      int n;      // bytes available there
      int q;      // output window write pointer
      int m;      // bytes to end of window or read pointer
      int f;      // pointer to copy strings from

      ZlibCodec z = blocks._codec;

      // copy input/output information to locals (UPDATE macro restores)
      p = z.NextIn;
      n = z.AvailableBytesIn;
      b = blocks.bitb;
      k = blocks.bitk;
      q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;

      // process input and output based on current state
      while (true)
      {
        switch (mode)
        {
          // waiting for "i:"=input, "o:"=output, "x:"=nothing
          case START:  // x: set up for LEN
            if (m >= 258 && n >= 10)
            {
              blocks.bitb = b; blocks.bitk = k;
              z.AvailableBytesIn = n;
              z.TotalBytesIn += p - z.NextIn;
              z.NextIn = p;
              blocks.writeAt = q;
              r = InflateFast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, blocks, z);

              p = z.NextIn;
              n = z.AvailableBytesIn;
              b = blocks.bitb;
              k = blocks.bitk;
              q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;

              if (r != ZlibConstants.Z_OK)
              {
                mode = (r == ZlibConstants.Z_STREAM_END) ? WASH : BADCODE;
                break;
              }
            }
            need = lbits;
            tree = ltree;
            tree_index = ltree_index;

            mode = LEN;
            goto case LEN;

          case LEN:  // i: get length/literal/eob next
            j = need;

            while (k < j)
            {
              if (n != 0)
                r = ZlibConstants.Z_OK;
              else
              {
                blocks.bitb = b; blocks.bitk = k;
                z.AvailableBytesIn = n;
                z.TotalBytesIn += p - z.NextIn;
                z.NextIn = p;
                blocks.writeAt = q;
                return blocks.Flush(r);
              }
              n--;
              b |= (z.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3;

            b >>= (tree[tindex + 1]);
            k -= (tree[tindex + 1]);

            e = tree[tindex];

            if (e == 0)
            {
              // literal
              lit = tree[tindex + 2];
              mode = LIT;
              break;
            }
            if ((e & 16) != 0)
            {
              // length
              bitsToGet = e & 15;
              len = tree[tindex + 2];
              mode = LENEXT;
              break;
            }
            if ((e & 64) == 0)
            {
              // next table
              need = e;
              tree_index = tindex / 3 + tree[tindex + 2];
              break;
            }
            if ((e & 32) != 0)
            {
              // end of block
              mode = WASH;
              break;
            }
            mode = BADCODE; // invalid code
            z.Message = "invalid literal/length code";
            r = ZlibConstants.Z_DATA_ERROR;

            blocks.bitb = b; blocks.bitk = k;
            z.AvailableBytesIn = n;
            z.TotalBytesIn += p - z.NextIn;
            z.NextIn = p;
            blocks.writeAt = q;
            return blocks.Flush(r);


          case LENEXT:  // i: getting length extra (have base)
            j = bitsToGet;

            while (k < j)
            {
              if (n != 0)
                r = ZlibConstants.Z_OK;
              else
              {
                blocks.bitb = b; blocks.bitk = k;
                z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
                blocks.writeAt = q;
                return blocks.Flush(r);
              }
              n--; b |= (z.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            len += (b & InternalInflateConstants.InflateMask[j]);

            b >>= j;
            k -= j;

            need = dbits;
            tree = dtree;
            tree_index = dtree_index;
            mode = DIST;
            goto case DIST;

          case DIST:  // i: get distance next
            j = need;

            while (k < j)
            {
              if (n != 0)
                r = ZlibConstants.Z_OK;
              else
              {
                blocks.bitb = b; blocks.bitk = k;
                z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
                blocks.writeAt = q;
                return blocks.Flush(r);
              }
              n--; b |= (z.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3;

            b >>= tree[tindex + 1];
            k -= tree[tindex + 1];

            e = (tree[tindex]);
            if ((e & 0x10) != 0)
            {
              // distance
              bitsToGet = e & 15;
              dist = tree[tindex + 2];
              mode = DISTEXT;
              break;
            }
            if ((e & 64) == 0)
            {
              // next table
              need = e;
              tree_index = tindex / 3 + tree[tindex + 2];
              break;
            }
            mode = BADCODE; // invalid code
            z.Message = "invalid distance code";
            r = ZlibConstants.Z_DATA_ERROR;

            blocks.bitb = b; blocks.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            blocks.writeAt = q;
            return blocks.Flush(r);


          case DISTEXT:  // i: getting distance extra
            j = bitsToGet;

            while (k < j)
            {
              if (n != 0)
                r = ZlibConstants.Z_OK;
              else
              {
                blocks.bitb = b; blocks.bitk = k;
                z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
                blocks.writeAt = q;
                return blocks.Flush(r);
              }
              n--; b |= (z.InputBuffer[p++] & 0xff) << k;
              k += 8;
            }

            dist += (b & InternalInflateConstants.InflateMask[j]);

            b >>= j;
            k -= j;

            mode = COPY;
            goto case COPY;

          case COPY:  // o: copying bytes in window, waiting for space
            f = q - dist;
            while (f < 0)
            {
              // modulo window size-"while" instead
              f += blocks.end; // of "if" handles invalid distances
            }
            while (len != 0)
            {
              if (m == 0)
              {
                if (q == blocks.end && blocks.readAt != 0)
                {
                  q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
                }
                if (m == 0)
                {
                  blocks.writeAt = q; r = blocks.Flush(r);
                  q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;

                  if (q == blocks.end && blocks.readAt != 0)
                  {
                    q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
                  }

                  if (m == 0)
                  {
                    blocks.bitb = b; blocks.bitk = k;
                    z.AvailableBytesIn = n;
                    z.TotalBytesIn += p - z.NextIn;
                    z.NextIn = p;
                    blocks.writeAt = q;
                    return blocks.Flush(r);
                  }
                }
              }

              blocks.window[q++] = blocks.window[f++]; m--;

              if (f == blocks.end)
                f = 0;
              len--;
            }
            mode = START;
            break;

          case LIT:  // o: got literal, waiting for output space
            if (m == 0)
            {
              if (q == blocks.end && blocks.readAt != 0)
              {
                q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
              }
              if (m == 0)
              {
                blocks.writeAt = q; r = blocks.Flush(r);
                q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;

                if (q == blocks.end && blocks.readAt != 0)
                {
                  q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;
                }
                if (m == 0)
                {
                  blocks.bitb = b; blocks.bitk = k;
                  z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
                  blocks.writeAt = q;
                  return blocks.Flush(r);
                }
              }
            }
            r = ZlibConstants.Z_OK;

            blocks.window[q++] = (byte)lit; m--;

            mode = START;
            break;

          case WASH:  // o: got eob, possibly more output
            if (k > 7)
            {
              // return unused byte, if any
              k -= 8;
              n++;
              p--; // can always return one
            }

            blocks.writeAt = q; r = blocks.Flush(r);
            q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q;

            if (blocks.readAt != blocks.writeAt)
            {
              blocks.bitb = b; blocks.bitk = k;
              z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
              blocks.writeAt = q;
              return blocks.Flush(r);
            }
            mode = END;
            goto case END;

          case END:
            r = ZlibConstants.Z_STREAM_END;
            blocks.bitb = b; blocks.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            blocks.writeAt = q;
            return blocks.Flush(r);

          case BADCODE:  // x: got error

            r = ZlibConstants.Z_DATA_ERROR;

            blocks.bitb = b; blocks.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            blocks.writeAt = q;
            return blocks.Flush(r);

          default:
            r = ZlibConstants.Z_STREAM_ERROR;

            blocks.bitb = b; blocks.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            blocks.writeAt = q;
            return blocks.Flush(r);
        }
      }
    }


    // Called with number of bytes left to write in window at least 258
    // (the maximum string length) and number of input bytes available
    // at least ten.  The ten bytes are six bytes for the longest length/
    // distance pair plus four bytes for overloading the bit buffer.

    internal int InflateFast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InflateBlocks s, ZlibCodec z)
    {
      int t;        // temporary pointer
      int[] tp;     // temporary pointer
      int tp_index; // temporary pointer
      int e;        // extra bits or operation
      int b;        // bit buffer
      int k;        // bits in bit buffer
      int p;        // input data pointer
      int n;        // bytes available there
      int q;        // output window write pointer
      int m;        // bytes to end of window or read pointer
      int ml;       // mask for literal/length tree
      int md;       // mask for distance tree
      int c;        // bytes to copy
      int d;        // distance back to copy from
      int r;        // copy source pointer

      int tp_index_t_3; // (tp_index+t)*3

      // load input, output, bit values
      p = z.NextIn; n = z.AvailableBytesIn; b = s.bitb; k = s.bitk;
      q = s.writeAt; m = q < s.readAt ? s.readAt - q - 1 : s.end - q;

      // initialize masks
      ml = InternalInflateConstants.InflateMask[bl];
      md = InternalInflateConstants.InflateMask[bd];

      // do until not enough input or output space for fast loop
      do
      {
        // assume called with m >= 258 && n >= 10
        // get literal/length code
        while (k < (20))
        {
          // max bits for literal/length code
          n--;
          b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
        }

        t = b & ml;
        tp = tl;
        tp_index = tl_index;
        tp_index_t_3 = (tp_index + t) * 3;
        if ((e = tp[tp_index_t_3]) == 0)
        {
          b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);

          s.window[q++] = (byte)tp[tp_index_t_3 + 2];
          m--;
          continue;
        }
        do
        {

          b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);

          if ((e & 16) != 0)
          {
            e &= 15;
            c = tp[tp_index_t_3 + 2] + ((int)b & InternalInflateConstants.InflateMask[e]);

            b >>= e; k -= e;

            // decode distance base of block to copy
            while (k < 15)
            {
              // max bits for distance code
              n--;
              b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
            }

            t = b & md;
            tp = td;
            tp_index = td_index;
            tp_index_t_3 = (tp_index + t) * 3;
            e = tp[tp_index_t_3];

            do
            {

              b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);

              if ((e & 16) != 0)
              {
                // get extra bits to add to distance base
                e &= 15;
                while (k < e)
                {
                  // get extra bits (up to 13)
                  n--;
                  b |= (z.InputBuffer[p++] & 0xff) << k; k += 8;
                }

                d = tp[tp_index_t_3 + 2] + (b & InternalInflateConstants.InflateMask[e]);

                b >>= e; k -= e;

                // do the copy
                m -= c;
                if (q >= d)
                {
                  // offset before dest
                  //  just copy
                  r = q - d;
                  if (q - r > 0 && 2 > (q - r))
                  {
                    s.window[q++] = s.window[r++]; // minimum count is three,
                    s.window[q++] = s.window[r++]; // so unroll loop a little
                    c -= 2;
                  }
                  else
                  {
                    Array.Copy(s.window, r, s.window, q, 2);
                    q += 2; r += 2; c -= 2;
                  }
                }
                else
                {
                  // else offset after destination
                  r = q - d;
                  do
                  {
                    r += s.end; // force pointer in window
                  }
                  while (r < 0); // covers invalid distances
                  e = s.end - r;
                  if (c > e)
                  {
                    // if source crosses,
                    c -= e; // wrapped copy
                    if (q - r > 0 && e > (q - r))
                    {
                      do
                      {
                        s.window[q++] = s.window[r++];
                      }
                      while (--e != 0);
                    }
                    else
                    {
                      Array.Copy(s.window, r, s.window, q, e);
                      q += e; r += e; e = 0;
                    }
                    r = 0; // copy rest from start of window
                  }
                }

                // copy all or what's left
                if (q - r > 0 && c > (q - r))
                {
                  do
                  {
                    s.window[q++] = s.window[r++];
                  }
                  while (--c != 0);
                }
                else
                {
                  Array.Copy(s.window, r, s.window, q, c);
                  q += c; r += c; c = 0;
                }
                break;
              }
              else if ((e & 64) == 0)
              {
                t += tp[tp_index_t_3 + 2];
                t += (b & InternalInflateConstants.InflateMask[e]);
                tp_index_t_3 = (tp_index + t) * 3;
                e = tp[tp_index_t_3];
              }
              else
              {
                z.Message = "invalid distance code";

                c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);

                s.bitb = b; s.bitk = k;
                z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
                s.writeAt = q;

                return ZlibConstants.Z_DATA_ERROR;
              }
            }
            while (true);
            break;
          }

          if ((e & 64) == 0)
          {
            t += tp[tp_index_t_3 + 2];
            t += (b & InternalInflateConstants.InflateMask[e]);
            tp_index_t_3 = (tp_index + t) * 3;
            if ((e = tp[tp_index_t_3]) == 0)
            {
              b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]);
              s.window[q++] = (byte)tp[tp_index_t_3 + 2];
              m--;
              break;
            }
          }
          else if ((e & 32) != 0)
          {
            c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);

            s.bitb = b; s.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            s.writeAt = q;

            return ZlibConstants.Z_STREAM_END;
          }
          else
          {
            z.Message = "invalid literal/length code";

            c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);

            s.bitb = b; s.bitk = k;
            z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
            s.writeAt = q;

            return ZlibConstants.Z_DATA_ERROR;
          }
        }
        while (true);
      }
      while (m >= 258 && n >= 10);

      // not enough input or output--restore pointers and return
      c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3);

      s.bitb = b; s.bitk = k;
      z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p;
      s.writeAt = q;

      return ZlibConstants.Z_OK;
    }
  }


  internal sealed class InflateManager
  {
    // preset dictionary flag in zlib header
    private const int PRESET_DICT = 0x20;

    private const int Z_DEFLATED = 8;

    private enum InflateManagerMode
    {
      METHOD = 0,  // waiting for method byte
      FLAG = 1,  // waiting for flag byte
      DICT4 = 2,  // four dictionary check bytes to go
      DICT3 = 3,  // three dictionary check bytes to go
      DICT2 = 4,  // two dictionary check bytes to go
      DICT1 = 5,  // one dictionary check byte to go
      DICT0 = 6,  // waiting for inflateSetDictionary
      BLOCKS = 7,  // decompressing blocks
      CHECK4 = 8,  // four check bytes to go
      CHECK3 = 9,  // three check bytes to go
      CHECK2 = 10, // two check bytes to go
      CHECK1 = 11, // one check byte to go
      DONE = 12, // finished check, done
      BAD = 13, // got an error--stay here
    }

    private InflateManagerMode mode; // current inflate mode
    internal ZlibCodec _codec; // pointer back to this zlib stream

    // mode dependent information
    internal int method; // if FLAGS, method byte

    // if CHECK, check values to compare
    internal uint computedCheck; // computed check value
    internal uint expectedCheck; // stream check value

    // if BAD, inflateSync's marker bytes count
    internal int marker;

    // mode independent information
    //internal int nowrap; // flag for no wrapper
    private bool _handleRfc1950HeaderBytes = true;
    internal bool HandleRfc1950HeaderBytes
    {
      get { return _handleRfc1950HeaderBytes; }
      set { _handleRfc1950HeaderBytes = value; }
    }
    internal int wbits; // log2(window size)  (8..15, defaults to 15)

    internal InflateBlocks blocks; // current inflate_blocks state

    public InflateManager() { }

    public InflateManager(bool expectRfc1950HeaderBytes)
    {
      _handleRfc1950HeaderBytes = expectRfc1950HeaderBytes;
    }

    internal int Reset()
    {
      _codec.TotalBytesIn = _codec.TotalBytesOut = 0;
      _codec.Message = null;
      mode = HandleRfc1950HeaderBytes ? InflateManagerMode.METHOD : InflateManagerMode.BLOCKS;
      blocks.Reset();
      return ZlibConstants.Z_OK;
    }

    internal int End()
    {
      if (blocks != null)
        blocks.Free();
      blocks = null;
      return ZlibConstants.Z_OK;
    }

    internal int Initialize(ZlibCodec codec, int w)
    {
      _codec = codec;
      _codec.Message = null;
      blocks = null;

      // handle undocumented nowrap option (no zlib header or check)
      //nowrap = 0;
      //if (w < 0)
      //{
      //    w = - w;
      //    nowrap = 1;
      //}

      // set window size
      if (w < 8 || w > 15)
      {
        End();
        throw new ZlibException("Bad window size.");

        //return ZlibConstants.Z_STREAM_ERROR;
      }
      wbits = w;

      blocks = new InflateBlocks(codec,
          HandleRfc1950HeaderBytes ? this : null,
          1 << w);

      // reset state
      Reset();
      return ZlibConstants.Z_OK;
    }


    internal int Inflate(FlushType flush)
    {
      int b;

      if (_codec.InputBuffer == null)
        throw new ZlibException("InputBuffer is null. ");

      //             int f = (flush == FlushType.Finish)
      //                 ? ZlibConstants.Z_BUF_ERROR
      //                 : ZlibConstants.Z_OK;

      // workitem 8870
      int f = ZlibConstants.Z_OK;
      int r = ZlibConstants.Z_BUF_ERROR;

      while (true)
      {
        switch (mode)
        {
          case InflateManagerMode.METHOD:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            if (((method = _codec.InputBuffer[_codec.NextIn++]) & 0xf) != Z_DEFLATED)
            {
              mode = InflateManagerMode.BAD;
              _codec.Message = String.Format("unknown compression method (0x{0:X2})", method);
              marker = 5; // can't try inflateSync
              break;
            }
            if ((method >> 4) + 8 > wbits)
            {
              mode = InflateManagerMode.BAD;
              _codec.Message = String.Format("invalid window size ({0})", (method >> 4) + 8);
              marker = 5; // can't try inflateSync
              break;
            }
            mode = InflateManagerMode.FLAG;
            break;


          case InflateManagerMode.FLAG:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            b = (_codec.InputBuffer[_codec.NextIn++]) & 0xff;

            if ((((method << 8) + b) % 31) != 0)
            {
              mode = InflateManagerMode.BAD;
              _codec.Message = "incorrect header check";
              marker = 5; // can't try inflateSync
              break;
            }

            mode = ((b & PRESET_DICT) == 0)
                ? InflateManagerMode.BLOCKS
                : InflateManagerMode.DICT4;
            break;

          case InflateManagerMode.DICT4:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000);
            mode = InflateManagerMode.DICT3;
            break;

          case InflateManagerMode.DICT3:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000);
            mode = InflateManagerMode.DICT2;
            break;

          case InflateManagerMode.DICT2:

            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00);
            mode = InflateManagerMode.DICT1;
            break;


          case InflateManagerMode.DICT1:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--; _codec.TotalBytesIn++;
            expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff);
            _codec._Adler32 = expectedCheck;
            mode = InflateManagerMode.DICT0;
            return ZlibConstants.Z_NEED_DICT;


          case InflateManagerMode.DICT0:
            mode = InflateManagerMode.BAD;
            _codec.Message = "need dictionary";
            marker = 0; // can try inflateSync
            return ZlibConstants.Z_STREAM_ERROR;


          case InflateManagerMode.BLOCKS:
            r = blocks.Process(r);
            if (r == ZlibConstants.Z_DATA_ERROR)
            {
              mode = InflateManagerMode.BAD;
              marker = 0; // can try inflateSync
              break;
            }

            if (r == ZlibConstants.Z_OK) r = f;

            if (r != ZlibConstants.Z_STREAM_END)
              return r;

            r = f;
            computedCheck = blocks.Reset();
            if (!HandleRfc1950HeaderBytes)
            {
              mode = InflateManagerMode.DONE;
              return ZlibConstants.Z_STREAM_END;
            }
            mode = InflateManagerMode.CHECK4;
            break;

          case InflateManagerMode.CHECK4:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000);
            mode = InflateManagerMode.CHECK3;
            break;

          case InflateManagerMode.CHECK3:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--; _codec.TotalBytesIn++;
            expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000);
            mode = InflateManagerMode.CHECK2;
            break;

          case InflateManagerMode.CHECK2:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--;
            _codec.TotalBytesIn++;
            expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00);
            mode = InflateManagerMode.CHECK1;
            break;

          case InflateManagerMode.CHECK1:
            if (_codec.AvailableBytesIn == 0) return r;
            r = f;
            _codec.AvailableBytesIn--; _codec.TotalBytesIn++;
            expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff);
            if (computedCheck != expectedCheck)
            {
              mode = InflateManagerMode.BAD;
              _codec.Message = "incorrect data check";
              marker = 5; // can't try inflateSync
              break;
            }
            mode = InflateManagerMode.DONE;
            return ZlibConstants.Z_STREAM_END;

          case InflateManagerMode.DONE:
            return ZlibConstants.Z_STREAM_END;

          case InflateManagerMode.BAD:
            throw new ZlibException(String.Format("Bad state ({0})", _codec.Message));

          default:
            throw new ZlibException("Stream error.");

        }
      }
    }



    internal int SetDictionary(byte[] dictionary)
    {
      int index = 0;
      int length = dictionary.Length;
      if (mode != InflateManagerMode.DICT0)
        throw new ZlibException("Stream error.");

      if (Adler.Adler32(1, dictionary, 0, dictionary.Length) != _codec._Adler32)
      {
        return ZlibConstants.Z_DATA_ERROR;
      }

      _codec._Adler32 = Adler.Adler32(0, null, 0, 0);

      if (length >= (1 << wbits))
      {
        length = (1 << wbits) - 1;
        index = dictionary.Length - length;
      }
      blocks.SetDictionary(dictionary, index, length);
      mode = InflateManagerMode.BLOCKS;
      return ZlibConstants.Z_OK;
    }


    private static readonly byte[] mark = new byte[] { 0, 0, 0xff, 0xff };

    internal int Sync()
    {
      int n; // number of bytes to look at
      int p; // pointer to bytes
      int m; // number of marker bytes found in a row
      long r, w; // temporaries to save total_in and total_out

      // set up
      if (mode != InflateManagerMode.BAD)
      {
        mode = InflateManagerMode.BAD;
        marker = 0;
      }
      if ((n = _codec.AvailableBytesIn) == 0)
        return ZlibConstants.Z_BUF_ERROR;
      p = _codec.NextIn;
      m = marker;

      // search
      while (n != 0 && m < 4)
      {
        if (_codec.InputBuffer[p] == mark[m])
        {
          m++;
        }
        else if (_codec.InputBuffer[p] != 0)
        {
          m = 0;
        }
        else
        {
          m = 4 - m;
        }
        p++; n--;
      }

      // restore
      _codec.TotalBytesIn += p - _codec.NextIn;
      _codec.NextIn = p;
      _codec.AvailableBytesIn = n;
      marker = m;

      // return no joy or set up to restart on a new block
      if (m != 4)
      {
        return ZlibConstants.Z_DATA_ERROR;
      }
      r = _codec.TotalBytesIn;
      w = _codec.TotalBytesOut;
      Reset();
      _codec.TotalBytesIn = r;
      _codec.TotalBytesOut = w;
      mode = InflateManagerMode.BLOCKS;
      return ZlibConstants.Z_OK;
    }


    // Returns true if inflate is currently at the end of a block generated
    // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
    // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
    // but removes the length bytes of the resulting empty stored block. When
    // decompressing, PPP checks that at the end of input packet, inflate is
    // waiting for these length bytes.
    internal int SyncPoint(ZlibCodec z)
    {
      return blocks.SyncPoint();
    }
  }
}